﻿// (c) Copyright Fernando Cerqueira.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Collections;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using System.Diagnostics.CodeAnalysis;

// ReSharper disable CheckNamespace
namespace DevPhone.Controls
// ReSharper restore CheckNamespace
{
    [TemplateVisualState(Name = "Enabled", GroupName = "ViewStates")]
    [TemplateVisualState(Name = "Disabled", GroupName = "ViewStates")]
    [TemplatePart(Name = "Header", Type = typeof(ContentControl))]
    [TemplatePart(Name = "SeletedContent", Type = typeof(ContentControl))]
    [TemplatePart(Name = "HostItemPresenter", Type = typeof(Border))]
    public class CheckPickerPageControl : ItemsControl
    {

        #region events

        public event EventHandler<PagePickerOpenChangeEventArgs> OpenPagePicker;
        public event EventHandler<SelectionChangedEventArgs> SelectionChange;
        public event EventHandler<SummaryConverterEventArgs> SummaryConverter;

        public void OnSummaryConverter(SummaryConverterEventArgs e)
        {
            var handler = SummaryConverter;
            if (handler != null) handler(this, e);
        }

        public void OnChangeOpen(PagePickerOpenChangeEventArgs e)
        {
            var handler = OpenPagePicker;
            if (handler != null) handler(this, e);
        }

        public void OnSelectionChange(SelectionChangedEventArgs e)
        {
            var handler = SelectionChange;
            if (handler != null) handler(this, e);
        }

        #endregion

        #region private fields

        //flag loaded contrlol
        private bool _isLoadedTemplate;
        private Grid _tapElementSeleted;
        private ContentControl _seletedContentElement;
        private static Brush _defaultBorder;
        private static Thickness _defaultBorderThickness;
        private static Brush _defaultDisabled;
        private static Brush _defaulControlBackground;
        private static Brush _defaulControlForeground;
        private bool _hasPickerPageOpen;

        private PhoneApplicationFrame _frame;
        private object _frameContentWhenOpened;
        private CheckPickerPage _pickerPage;
        private NavigationInTransition _savedNavigationInTransition;
        private NavigationOutTransition _savedNavigationOutTransition;
        private SupportedPageOrientation _hostSupportedPageOrientation;
        private PageOrientation _hostCurrentOrientation;

        #endregion

        #region Dependency Properties

        // InheritOrientation

        public static readonly DependencyProperty InheritOrientationProperty =
            DependencyProperty.Register("InheritOrientation", typeof(bool), typeof(CheckPickerPageControl), new PropertyMetadata(true));

        public bool InheritOrientation
        {
            get { return (bool)GetValue(InheritOrientationProperty); }
            set { SetValue(InheritOrientationProperty, value); }
        }

        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(object), typeof(CheckPickerPageControl), new PropertyMetadata(null));

        public object Header
        {
            get { return GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        public static readonly DependencyProperty HeaderTemplateProperty =
            DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(CheckPickerPageControl), new PropertyMetadata(null));

        public DataTemplate HeaderTemplate
        {
            get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
            set { SetValue(HeaderTemplateProperty, value); }
        }

        public static readonly DependencyProperty SelectedTemplateProperty =
            DependencyProperty.Register("SelectedTemplate", typeof(DataTemplate), typeof(CheckPickerPageControl), new PropertyMetadata(null, null));

        public DataTemplate SelectedTemplate
        {
            get { return (DataTemplate)GetValue(SelectedTemplateProperty); }
            set
            {
                SetValue(SelectedTemplateProperty, value);
            }
        }

        /// <summary>
        /// Identifies the PageHeader DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty PageHeaderProperty =
            DependencyProperty.Register("PageHeader", typeof(object), typeof(CheckPickerPageControl), null);


        /// <summary>
        /// Gets or sets the header page.
        /// </summary>
        public object PageHeader
        {
            get { return GetValue(PageHeaderProperty); }
            set { SetValue(PageHeaderProperty, value); }
        }


        public static readonly DependencyProperty PageHeaderTemplateProperty =
            DependencyProperty.Register("PageHeaderTemplate", typeof(DataTemplate), typeof(CheckPickerPageControl), new PropertyMetadata(null));

        public DataTemplate PageHeaderTemplate
        {
            get { return (DataTemplate)GetValue(PageHeaderTemplateProperty); }
            set { SetValue(PageHeaderTemplateProperty, value); }
        }

        public static readonly DependencyProperty PageHeaderDataContextProperty =
            DependencyProperty.Register("PageHeaderDataContext", typeof(object), typeof(CheckPickerPageControl), new PropertyMetadata(null));

        public object PageHeaderDataContext
        {
            get { return GetValue(PageHeaderDataContextProperty); }
            set { SetValue(PageHeaderDataContextProperty, value); }
        }

        /// <summary>
        /// Identifies the PageImageBackgroundProperty DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty PageImageBackgroundProperty =
            DependencyProperty.Register("PageImageBackground", typeof(ImageBrush), typeof(CheckPickerPageControl), new PropertyMetadata(null, null));

        /// <summary>
        /// Gets or sets the image page background.
        /// </summary>
        public ImageBrush PageImageBackground
        {
            get { return (ImageBrush)GetValue(PageImageBackgroundProperty); }
            set { SetValue(PageImageBackgroundProperty, value); }
        }

        /// <summary>
        /// Identifies the PageBackgroundProperty DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty PageBackgroundProperty =
            DependencyProperty.Register("PageBackground", typeof(Brush), typeof(CheckPickerPageControl), new PropertyMetadata(null, null));

        /// <summary>
        /// Gets or sets the page page background.
        /// </summary>
        public Brush PageBackground
        {
            get { return (Brush)GetValue(PageBackgroundProperty); }
            set { SetValue(PageBackgroundProperty, value); }
        }

        public static readonly DependencyProperty ControlBackgroundProperty =
            DependencyProperty.Register("ControlBackground", typeof(Brush), typeof(CheckPickerPageControl), new PropertyMetadata(null, ControlBackgroundChangedCallback));

        private static void ControlBackgroundChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CheckPickerPageControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.ControlBackground = _defaulControlBackground;
            }
            instance.Background = instance.ControlBackground;
            if (instance._isLoadedTemplate)
            {
                instance._tapElementSeleted.Background = instance.ControlBackground;
            }
        }

        public Brush ControlBackground
        {
            get { return (Brush)GetValue(ControlBackgroundProperty); }
            set
            {
                SetValue(ControlBackgroundProperty, value);
                SetValue(BackgroundProperty, value);
            }
        }

        public static readonly DependencyProperty ControlForegroundProperty =
            DependencyProperty.Register("ControlForeground", typeof(Brush), typeof(CheckPickerPageControl), new PropertyMetadata(null, ControlForegroundChangedCallback));

        private static void ControlForegroundChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CheckPickerPageControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.ControlForeground = _defaulControlForeground;
            }
            instance.ControlForeground = instance.ControlForeground;
            if (instance._isLoadedTemplate)
            {
                instance._seletedContentElement.Foreground = instance.ControlForeground;
            }
        }

        public Brush ControlForeground
        {
            get { return (Brush)GetValue(ControlForegroundProperty); }
            set
            {
                SetValue(ControlForegroundProperty, value);
                SetValue(ForegroundProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the Uri to use for loading the ListPickerPage instance when the control is tapped.
        /// </summary>
        public Uri PickerPageUri
        {
            get { return (Uri)GetValue(PickerPageUriProperty); }
            set { SetValue(PickerPageUriProperty, value); }
        }

        /// <summary>
        /// Identifies the PickerPageUri DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty PickerPageUriProperty = DependencyProperty.Register(
            "PickerPageUri", typeof(Uri), typeof(CheckPickerPageControl), null);

        /// <summary>
        /// Identifies the SelectedItem DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty SelectedItemsProperty =
            DependencyProperty.Register("SelectedItems", typeof(IList), typeof(CheckPickerPageControl), new PropertyMetadata(null, SelectedItemsChangedCallback));

        private static void SelectedItemsChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == null)
            {
                return;
            }
            var instance = (CheckPickerPageControl)dependencyObject;
            if (instance._isLoadedTemplate)
            {
                if (null == instance.Items || instance.Items.Count == 0)
                {
                    instance.SelectedItems = null;
                }
                instance.UpdateSumary();
            }
        }

        [TypeConverter(typeof(SelectedItemsTypeConverter))]
        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public IList SelectedItems
        {
            get 
            {
                return (IList)GetValue(SelectedItemsProperty); 
            }
            set
            {
                SetValue(SelectedItemsProperty, value);
            }
        }

        public static readonly DependencyProperty DisabledBrushProperty =
            DependencyProperty.Register("DisabledBrush", typeof(Brush), typeof(CheckPickerPageControl), new PropertyMetadata(null, DisabledBrushChangedCallback));

        private static void DisabledBrushChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CheckPickerPageControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.DisabledBrush = _defaultDisabled;
            }
            if (instance._isLoadedTemplate && !instance.IsEnabled)
            {
                instance._seletedContentElement.Foreground = instance.DisabledBrush;
            }
        }

        public Brush DisabledBrush
        {
            get { return (Brush)GetValue(DisabledBrushProperty); }
            set
            {
                SetValue(DisabledBrushProperty, value);
            }
        }

        private static readonly DependencyProperty PreviewInDesignTimeProperty =
            DependencyProperty.Register("PreviewInDesignTime", typeof(bool), typeof(CheckPickerPageControl), new PropertyMetadata(false, PreviewInDesignTimePropertyChangedCallback));

        public bool PreviewInDesignTime
        {
            get { return (bool)GetValue(PreviewInDesignTimeProperty); }
            set { SetValue(PreviewInDesignTimeProperty, value); }
        }

        private static void PreviewInDesignTimePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var instance = (CheckPickerPageControl)d;
            instance.UpdateSumary();
        }


        private static readonly DependencyProperty AppBarPageCheckOptionsProperty =
            DependencyProperty.Register("AppBarPageCheckOptions", typeof(bool), typeof(CheckPickerPageControl), new PropertyMetadata(true));

        public bool AppBarPageCheckOptions
        {
            get { return (bool)GetValue(AppBarPageCheckOptionsProperty); }
            set { SetValue(AppBarPageCheckOptionsProperty, value); }
        }

        #endregion

        #region Load / template / VisualState / SizeChanged

        public CheckPickerPageControl()
        {
            DefaultStyleKey = typeof(CheckPickerPageControl);
            SizeChanged += CheckPickerPageControlSizeChanged;
            IsEnabledChanged += CheckPickerControlIsEnabledChanged;
        }

        private void CheckPickerControlIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            VisualStateManager.GoToState(this, IsEnabled ? "Enabled" : "Disabled", false);
            if (_seletedContentElement != null)
            {
                _seletedContentElement.Foreground = IsEnabled ? Foreground : DisabledBrush;
            }
        }

        private void CheckPickerPageControlSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (!double.IsNaN(Height))
            {
                Height = double.NaN;
            }
        }

        private void DoWorkOnRender(object sender, EventArgs args)
        {
            CompositionTarget.Rendering -= DoWorkOnRender;
            if (null == Items || Items.Count == 0)
            {
                SelectedItems = null;
            }
            if (_seletedContentElement != null & SelectedTemplate != null)
            {
                _seletedContentElement.ContentTemplate = SelectedTemplate;
            }
            UpdateSumary();
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _tapElementSeleted = (Grid)GetTemplateChild("AreaHeader");

            _seletedContentElement = (ContentControl)GetTemplateChild("SeletedContent");

            ClipBoundsControl.SetToBounds(_tapElementSeleted, true);

            _defaultBorder = (Brush)Application.Current.Resources["PhoneSubtleBrush"];
            _defaultDisabled = (Brush)Application.Current.Resources["PhoneTextBoxReadOnlyBrush"];
            _defaulControlBackground = (Brush)Application.Current.Resources["PhoneTextBoxBrush"];
            _defaulControlForeground = (Brush)Application.Current.Resources["PhoneTextBoxForegroundBrush"];
            _defaultBorderThickness = new Thickness(1, 1, 1, 1);

            if (ControlBackground == null)
            {
                ControlBackground = _defaulControlBackground;
            }

            if (ControlForeground == null)
            {
                ControlForeground = _defaulControlForeground;
            }

            if (DisabledBrush == null)
            {
                DisabledBrush = _defaultDisabled;
            }

            if (BorderBrush == null)
            {
                BorderBrush = _defaultBorder;
            }
            if (BorderThickness == new Thickness())
            {
                BorderThickness = _defaultBorderThickness;
            }

            if (_tapElementSeleted != null)
            {
                _tapElementSeleted.Tap += TapElementClick;
                _tapElementSeleted.Background = Background;
            }

            if (_seletedContentElement != null)
            {
                _seletedContentElement.Foreground = Foreground;
                _seletedContentElement.Background = Background;
            }

            // Translate it into view once layout has been updated
            Dispatcher.BeginInvoke(() =>
            {
                VisualStateManager.GoToState(this, IsEnabled ? "Enabled" : "Disabled", false);
                if (_seletedContentElement != null)
                {
                    _seletedContentElement.Foreground = IsEnabled ? Foreground : DisabledBrush;
                }
                _isLoadedTemplate = true;
                CompositionTarget.Rendering += DoWorkOnRender;
            });

        }

        #endregion

        #region private methods

        private void UpdateSumary()
        {
            if (DesignerProperties.IsInDesignTool && !PreviewInDesignTime)
            {
                if (_seletedContentElement != null)
                {
                    _seletedContentElement.Content = null;
                    _seletedContentElement.ContentTemplate = null;
                }
                return;
            }
            if (_seletedContentElement != null)
            {
                _seletedContentElement.ContentTemplate = SelectedTemplate;
            }
            if (null == SelectedTemplate || null == Items || Items.Count == 0 || null == SelectedItems || SelectedItems.Count == 0)
            {
                if (_seletedContentElement != null)
                {
                    _seletedContentElement.Content = null;
                    _seletedContentElement.ContentTemplate = null;
                }
                return;
            }
            if (SelectedItems != null)
            {
                var result = new List<object>();
                foreach (var selitem in SelectedItems)
                {
                    foreach (var item in Items)
                    {
                        var it = item as ListPickerItem;
                        if (it != null)
                        {
                            if (it.Content.Equals(selitem))
                            {
                                result.Add(selitem);
                                break;
                            }
                        }
                        else
                        {
                            if (item.Equals(selitem))
                            {
                                result.Add(selitem);
                                break;
                            }
                        }
                    }
                }
                if (SelectedTemplate != null)
                {
                    if (SummaryConverter != null)
                    {
                        var arg = new SummaryConverterEventArgs(result);
                        OnSummaryConverter(arg);
                        _seletedContentElement.Content = arg.ValueConverted;
                    }
                    else
                    {
                        _seletedContentElement.Content = result;
                    }
                }
            }
        }

        private void TapElementClick(object sender, RoutedEventArgs e)
        {
            if (null == Items || Items.Count == 0)
            {
                return;
            }
            OpenPickerPage();
        }

        private void OpenPickerPage()
        {
            if (null == PickerPageUri)
            {
                throw new ArgumentException("PickerPageUri");
            }

            if (null == _frame)
            {
                // Hook up to necessary events and navigate
                _frame = Application.Current.RootVisual as PhoneApplicationFrame;
                if (null != _frame)
                {
                    _frameContentWhenOpened = _frame.Content;

                    // Save and clear host page transitions for the upcoming "popup" navigation
                    var frameContentWhenOpenedAsUIElement = _frameContentWhenOpened as UIElement;

                    if (null != frameContentWhenOpenedAsUIElement)
                    {
                        _savedNavigationInTransition = TransitionService.GetNavigationInTransition(frameContentWhenOpenedAsUIElement);
                        TransitionService.SetNavigationInTransition(frameContentWhenOpenedAsUIElement, null);
                        _savedNavigationOutTransition = TransitionService.GetNavigationOutTransition(frameContentWhenOpenedAsUIElement);
                        TransitionService.SetNavigationOutTransition(frameContentWhenOpenedAsUIElement, null);

                    }
                    var pg = _frame.Content as PhoneApplicationPage;
                    _hostSupportedPageOrientation = SupportedPageOrientation.PortraitOrLandscape;
                    _hostCurrentOrientation = PageOrientation.None;
                    if (pg != null && InheritOrientation)
                    {
                        _hostSupportedPageOrientation = pg.SupportedOrientations;
                        _hostCurrentOrientation = pg.Orientation;
                    }

                    _frame.Navigated += OnFrameNavigated;
                    _hasPickerPageOpen = !_hasPickerPageOpen;
                    OnChangeOpen(new PagePickerOpenChangeEventArgs(_hasPickerPageOpen));
                    _frame.Navigate(PickerPageUri);
                }
            }
        }

        private void ClosePickerPage()
        {
            if (null != _frame)
            {
                _frame.Navigated -= OnFrameNavigated;
                // Restore host page transitions for the completed "popup" navigation
                var frameContentWhenOpenedAsUIElement = _frameContentWhenOpened as UIElement;
                if (null != frameContentWhenOpenedAsUIElement)
                {
                    TransitionService.SetNavigationInTransition(frameContentWhenOpenedAsUIElement, _savedNavigationInTransition);
                    TransitionService.SetNavigationOutTransition(frameContentWhenOpenedAsUIElement, _savedNavigationOutTransition);
                    _savedNavigationInTransition = null;
                    _savedNavigationOutTransition = null;
                }


                // Commit the value if available
                if (null != _pickerPage)
                {
                    if (null != _pickerPage.SelectedItems)
                    {
                        SelectedItems = _pickerPage.SelectedItems;
                    }
                    _pickerPage = null;
                }

                _frame = null;
                _frameContentWhenOpened = null;
                _hasPickerPageOpen = false;
                OnChangeOpen(new PagePickerOpenChangeEventArgs(_hasPickerPageOpen));
            }
        }

        private void OnFrameNavigated(object sender, NavigationEventArgs e)
        {
            if (e.Content == _frameContentWhenOpened)
            {
                ClosePickerPage();
            }
            else if (null == _pickerPage && _hasPickerPageOpen)
            {
                _pickerPage = e.Content as CheckPickerPage;
                if (null != _pickerPage)
                {
                    // Set up the list picker page with the necesarry fields.
                    if (null != PageHeader || null != PageHeaderTemplate)
                    {
                        _pickerPage.HeaderContent = null != PageHeaderTemplate ? null : PageHeader;
                    }
                    else
                    {
                        _pickerPage.HeaderContent = Header;
                    }

                    _pickerPage.SupportedOrientations = _hostSupportedPageOrientation;
                    _pickerPage.Orientation = _hostCurrentOrientation;

                    _pickerPage.PageBackground = PageBackground ?? new SolidColorBrush(Colors.Transparent);
                    _pickerPage.PageImageBackground = PageImageBackground;
                    _pickerPage.PageHeaderTemplate = PageHeaderTemplate;
                    _pickerPage.PageHeaderDataContext = PageHeaderDataContext;
                    _pickerPage.PageItemTemplate = ItemTemplate;
                    _pickerPage.AppBarPageCheckOptions = AppBarPageCheckOptions;
                    _pickerPage.Items.Clear();
                    if (null != Items)
                    {
                        foreach (var t in Items)
                        {
                            var item = t as ListPickerItem;
                            if (item != null && item.Content != null)
                            {
                                _pickerPage.Items.Add(item.Content);
                            }
                            else
                            {
                                _pickerPage.Items.Add(t);
                            }
                        }
                    }
                    if (null != Items && SelectedItems != null)
                    {
                        _pickerPage.SelectedItems = SelectedItems;
                    }
                }
            }
        }

        #endregion

        #region override Items Container

        /// <summary>
        /// Determines if the specified item is (or is eligible to be) its own item container.
        /// </summary>
        /// <param name="item">The specified item.</param>
        /// <returns>True if the item is its own item container; otherwise, false.</returns>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is ListPickerItem;
        }

        /// <summary>
        /// Creates or identifies the element used to display a specified item.
        /// </summary>
        /// <returns>A container corresponding to a specified item.</returns>
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new ListPickerItem();
        }

        #endregion

    }

}
